import { DispatchCtrlLib } from './DispatchCtrlLib'
import { getUserMedia } from './adapter'
import { changesdp } from './tinySDP/tsdp_api'
import { changesdp_local } from './tinySDP/tsdp_api'
import { ScsConstants } from '../js/ScsConstants'


//————————————————————————————————————— 配置 ——————————————————————————————————————————
var DispatchRtc_DebugVideo = true;

/************************** DispatchRtcLib ******************************/
function DispatchRtcLib() {
    this.index = 0;
    this.CallID = 0;
    this.CallType = -1;

    this.peerconnection = null;
    this.LocalVideoCandidates = new Array();
    this.LocalAudioCandidates = new Array();

    this.LocalSdp = null;           //本地sdp

    this.LocalAudioStream = null;	//本地音频媒体stream
    this.LocalVideoStream = null;	//本地视频媒体stream
    this.LocalVideoLib = null;      //本地视频videolib

	this.RemoteStream = null;       //对端媒体stream
	this.RemoteSdp = null;          //对端sdp
    this.RemoteVideoLib = null;     //对端视频
    this.RemoteAudioLabel = null;   //audio标签 双向语音用

    this.AudioRTCRtpSender = null;
    this.VideoRTCRtpSender = null;
    this.CurrentCaptureDev = "none";

    this.bytesReceived = 0;
    this.framesDecoded = 0;
    this.frameWidth = 0;
    this.frameHeight = 0;

    this.CandidateTimeout = null;

	this.CurrentMicphone = null;
}

var DispatchRtcLib_Array = new Array;


var DispatchRtcLib_MoniterVideo = 0;
var DispatchRtcLib_VoipAudio = 1;
var DispatchRtcLib_VoipVideo = 2;

var DispatchRtcLib_ConfAudio = 3;
var DispatchRtcLib_ConfVideoPush = 4;
var DispatchRtcLib_ConfVideoPull = 5;

//————————————————————————————————————— DispatchDevices ——————————————————————————————————————————
var DispatchDevices = {
    DevicesList: null,              //全部设备列表

    SpeakerList: new Array,              //扬声器列表
    MicphoneList: new Array,             //麦克风列表
    CameraList: new Array,               //摄像头

    UsingSpeaker: null,
    UsingMicphone: null,
    UsingCamera: null,
};

//选择本地设备
DispatchDevices.setLocalDevice = function(Type, DevID)
{
	if (Type == 0)
	{
		//扬声器
		if (DispatchDevices.UsingSpeaker.deviceId == DevID)
		{
			return 0;
		}

		for (i = 0; i < DispatchDevices.SpeakerList.length; i++)
		{
		    var speaker = DispatchDevices.SpeakerList[i];
		    if (speaker.deviceId == DevID)
		    {
		        //扬声器
		        DispatchDevices.UsingSpeaker = speaker;
		        console.log("* 改变当前扬声器为" + DispatchDevices.UsingSpeaker.label);

				var msg = new Object;
				msg.Msg = "LocalDevSetReq";
				msg.Type = 0;
				msg.DevID = DispatchDevices.UsingSpeaker.label;
				if (DispatchDevices.UsingSpeaker.deviceId == "default")
				{
					var s = "默认 - ";  //定义字符串
					msg.DevID = DispatchDevices.UsingSpeaker.label.substring(s.length);
				}
				else if (DispatchDevices.UsingSpeaker.deviceId == "communications")
				{
					var s = "通讯 - ";  //定义字符串
					msg.DevID = DispatchDevices.UsingSpeaker.label.substring(s.length);
				}

				DispatchCtrlLib.WsSend(msg);

		        return 0;
		    }
		}

		return -1;
	}
	else if	(Type == 1)
	{
		//麦克风
		if (DispatchDevices.UsingMicphone.deviceId == DevID)
		{
			return 0;
		}

		for (i = 0; i < DispatchDevices.MicphoneList.length; i++)
		{
		    var micphone = DispatchDevices.MicphoneList[i];
		    if (micphone.deviceId == DevID)
		    {
		        //扬声器
		        DispatchDevices.UsingMicphone = micphone;
		        console.log("* 改变当前麦克风为" + DispatchDevices.UsingMicphone.label);

				var msg = new Object;
				msg.Msg = "LocalDevSetReq";
				msg.Type = 1;
				msg.DevID = DispatchDevices.UsingMicphone.label;
				if (DispatchDevices.UsingMicphone.deviceId == "default")
				{
					var s = "默认 - ";  //定义字符串
					msg.DevID = DispatchDevices.UsingMicphone.label.substring(s.length);
				}
				else if (DispatchDevices.UsingMicphone.deviceId == "communications")
				{
					var s = "通讯 - ";  //定义字符串
					msg.DevID = DispatchDevices.UsingMicphone.label.substring(s.length);
				}

				DispatchCtrlLib.WsSend(msg);

		        return 0;
		    }
		}

		return -1;
	}
	else if	(Type == 2)
	{
		//摄像头
		if (DispatchDevices.UsingCamera.deviceId == DevID)
		{
			return 0;
		}

		for (var i = 0; i < DispatchDevices.CameraList.length; i++)
		{
		    var speaker = DispatchDevices.CameraList[i];
		    if (speaker.deviceId == DevID)
		    {
		        //扬声器
		        DispatchDevices.UsingCamera = speaker;
		        console.log("* 改变当前扬声器为" + DispatchDevices.UsingCamera.label);
		        return 0;
		    }
		}

		return -1;
	}

	return -2;
}

//检测本地设备
DispatchDevices.checkLocalDevice = function(devicelist)
{
    DispatchDevices.DevicesList     = new Array;

    DispatchDevices.SpeakerList     = new Array;
    DispatchDevices.MicphoneList    = new Array;
    DispatchDevices.CameraList      = new Array;

	var getDevicesDone = function(devices)
	{
	    DispatchDevices.DevicesList = devices;
	    console.log("本地设备列表 开始 ——————————————————");

	    var UsingSpeakerChanged = true;
	    var UsingMicphoneChanged = true;
	    var UsingCameraChanged = true;

	    for (var i = 0; i < devices.length; i++)
	    {
	        var thisdev = devices[i];
	        var device = {
	            deviceId: thisdev.deviceId,
	            label: thisdev.label,
	            groupId: thisdev.groupId,
	            kind: thisdev.kind
	        };

	        if (device.label.indexOf(" - ") == 0)
	        {
	            //客户端没有显示默认、通讯
	            if (device.deviceId == "default")
	            {
	                device.label = "默认" + device.label;
	            }
	            else if (device.deviceId == "communications")
	            {
	                device.label = "通讯" + device.label;
	            }
	        }

	        if (device.label.length > 11 &&
	            device.label[device.label.length-1] == ")" &&
	            device.label[device.label.length-6] == ":" &&
	            device.label[device.label.length-11] == "(" &&
	            device.label[device.label.length-12] == " ")
	        {
	            device.label = device.label.substring(0, device.label.length - 12);
	        }

	        if (device.kind == "audiooutput")
	        {
	            //扬声器
                if (device.deviceId != "communications")
                {
                    DispatchDevices.SpeakerList.push(device);
                }
	            console.log("* 扬声器:" + device.label + " ID:" + device.deviceId);

	            if (DispatchDevices.UsingSpeaker &&
	                device.label == DispatchDevices.UsingSpeaker.label)
	            {
	                UsingSpeakerChanged = false;
	            }
	        }
	        else if (device.kind == "audioinput")
	        {
	            //麦克风
                if (device.deviceId != "communications")
                {
                    DispatchDevices.MicphoneList.push(device);
                }
	            console.log("* 麦克风:" + device.label + " ID:" + device.deviceId);

	            if (DispatchDevices.UsingMicphone &&
	                device.label == DispatchDevices.UsingMicphone.label)
	            {
	                UsingMicphoneChanged = false;
	            }
	        }
	        else if (device.kind == "videoinput")
	        {
	            //摄像头
	            DispatchDevices.CameraList.push(device);
	            console.log("* 摄像头:" + device.label + " ID:" + device.deviceId);

	            if (DispatchDevices.UsingCamera &&
	                device.label == DispatchDevices.UsingCamera.label)
	            {
	                UsingCameraChanged = false;
	            }
	        }
	    }

	    if (UsingSpeakerChanged)    //扬声器
	    {
	        for (i = 0; i < DispatchDevices.SpeakerList.length; i++)
	        {
	            var speaker = DispatchDevices.SpeakerList[i];
	            if (speaker.deviceId == "default")
	            {
	                //设备变更
	                var msg = new Object;
	                msg.Msg = "LocalDevSetReq";
	                msg.Type = 0;

	                var s = "默认 - ";  //定义字符串
	                msg.DevID = speaker.label.substring(s.length);

	                DispatchCtrlLib.WsSend(msg);

	                DispatchDevices.UsingSpeaker = speaker;
	                console.log("* 改变当前扬声器为" + DispatchDevices.UsingSpeaker.label);
	                break;
	            }
	        }

			if (DispatchDevices.SpeakerList.length == 0)
			{
				DispatchDevices.UsingSpeaker = null;
			}

	        if (DispatchDevices.UsingSpeaker == null)
	        {
	            console.log("* 无可用扬声器");

				var msg = new Object;
				msg.Msg = "LocalDevSetReq";
				msg.Type = 0;
				msg.DevID = "FakeDev";

				DispatchCtrlLib.WsSend(msg);
	        }
	    }

	    if (UsingMicphoneChanged)   //麦克风
	    {
	        for (i = 0; i < DispatchDevices.MicphoneList.length; i++)
	        {
	            var micro = DispatchDevices.MicphoneList[i];
	            if (micro.deviceId == "default")
	            {
	                //设备变更
	                var msg = new Object;
	                msg.Msg = "LocalDevSetReq";
	                msg.Type = 1;

	                var s = "默认 - ";  //定义字符串
	                msg.DevID = micro.label.substring(s.length);

	                DispatchCtrlLib.WsSend(msg);

	                DispatchDevices.UsingMicphone = micro;
	                console.log("* 改变当前麦克风为" + DispatchDevices.UsingMicphone.label);
	                break;
	            }
	        }

			if (DispatchDevices.MicphoneList.length == 0)
			{
				DispatchDevices.UsingMicphone = null;
			}

			if (DispatchDevices.UsingMicphone == null)
			{
			    console.log("* 无可用麦克风");

				var msg = new Object;
				msg.Msg = "LocalDevSetReq";
				msg.Type = 1;
				msg.DevID = "FakeDev";

				DispatchCtrlLib.WsSend(msg);
			}
	    }

	    if (UsingCameraChanged)
	    {
	        DispatchDevices.UsingCamera = null;
	        if (DispatchDevices.CameraList.length == 0)
	        {
	            console.log("* 无可用摄像头");
	        }
	        else
	        {
	            DispatchDevices.UsingCamera = DispatchDevices.CameraList[0];
	        }
	    }

	    console.log("本地设备列表 结束 ——————————————————");


	    if (DispatchDevices.SpeakerList.length > 0 &&
	        DispatchDevices.UsingSpeaker == null)
	    {
	        //获取不到权限的情况,尝试打开设备再关闭
	        var localmedia =
	        {
	            "audio": true,
	            "video": true
	        };

	        if (DispatchDevices.MicphoneList.length == 0)
	        {
	            localmedia.audio = false;
	        }

	        if (DispatchDevices.CameraList.length == 0)
	        {
	            localmedia.video = false;
	        }

	        if (localmedia.audio || localmedia.video)
	        {
	            alert("初次加载,测试本地媒体设备,请赋予权限.");
	            function success(stream)
	            {
	                for (var i = 0; i < stream.getTracks().length; i++)
	                {
	                    stream.getTracks()[i].stop();
	                }

	                setTimeout(DispatchDevices.checkLocalDevice(), 1000);
	            }

	            function error(error)
	            {
	                console.log("加载本地媒体设备失败,会影响使用.");
	            }

	            navigator.getUserMedia(localmedia, success, error);
	        }

	    }
	}

	if (devicelist)
	{
		getDevicesDone(devicelist);
	}
	else
	{
		navigator.mediaDevices.enumerateDevices()
		.then(getDevicesDone)
		.catch(function(error)
		{
		    console.log(error);

		});
	}
}

//————————————————————————————————————— DispatchRtcLib ——————————————————————————————————————————
DispatchRtcLib.Init = function()
{
    // 获取谷歌浏览器版本
    console.log("DispatchRtcLib.Init");

    function getChromeVersion() {
        var arr = navigator.userAgent.split(' ');
        var chromeVersion = '';
        for(var i=0;i < arr.length;i++){
            if(/chrome/i.test(arr[i]))
            chromeVersion = arr[i]
        }
        if(chromeVersion){
            return Number(chromeVersion.split('/')[1].split('.')[0]);
        } else {
            return false;
        }
    }

    if(getChromeVersion()) {
        var version = getChromeVersion();
        console.log("Chrome " + version);
    }

	function InitLocalMedia() {

		//生成本地虚拟头像
		DispatchRtcLib.LocalPhotoStream = null;

		var LocalPhoto_canvas = document.createElement('canvas');
		if (LocalPhoto_canvas.captureStream)
		{
			LocalPhoto_canvas.width = 320;
			LocalPhoto_canvas.height = 240;
			LocalPhoto_canvas.style.display = "none";
			document.body.appendChild(LocalPhoto_canvas);

			var ctx = LocalPhoto_canvas.getContext('2d');
			var img = new Image();
			img.onload = function()
			{
			    ctx.drawImage(img, 0, 0);
			    DispatchRtcLib.LocalPhotoStream = LocalPhoto_canvas.captureStream();
			    DispatchRtcLib.LocalPhotoStream.bLocalPhoto = true;
				DispatchRtcLib.LocalPhotoStream.getVideoTracks()[0].bLocalPhoto = true;

			    window.setInterval(function()
			    {
			        if (DispatchRtcLib.LocalPhotoStream)
			        {
			            ctx.drawImage(img, 0, 0);
			        }
			    }, 500);
			}
			img.src = "./LocalPhoto.jpg";
		}

		//媒体流信息处理
		window.setInterval(function() {
		    for (var i = 0; i < DispatchRtcLib_Array.length; i++)
		    {
		        if (DispatchRtcLib_Array[i] != null &&
		            DispatchRtcLib_Array[i].peerconnection &&
		            DispatchRtcLib_Array[i].RemoteVideoLib &&
                    DispatchRtcLib_Array[i].RemoteStream)
                {
                    if (DispatchRtcLib_Array[i].CallType == DispatchRtcLib_MoniterVideo ||
                        DispatchRtc_DebugVideo)
                    {
		            let rtcLib = DispatchRtcLib_Array[i];
		            rtcLib.peerconnection.getStats(null).then( (stats) => {

		                var bytes_sec = 0;
		                var frames_sec = 0;
						var packetsLost = 0;
						var packetsReceived = 0;

		                stats.forEach(report => {
		                    if (report.type == "inbound-rtp" &&
		                        report.mediaType == "video")
		                    {
		                        bytes_sec = report.bytesReceived - rtcLib.bytesReceived;
		                        frames_sec = report.framesDecoded - rtcLib.framesDecoded;

								packetsLost = report.packetsLost;
								packetsReceived = report.packetsReceived;

		                        rtcLib.bytesReceived = report.bytesReceived;
		                        rtcLib.framesDecoded = report.framesDecoded;
		                    }
		                    else if (report.type == "track" &&
		                             report.remoteSource &&
		                             report.kind == "video")
		                    {
		                        rtcLib.frameWidth = report.frameWidth;
		                        rtcLib.frameHeight = report.frameHeight;
		                    }
		                });

		                if (rtcLib.frameWidth && rtcLib.frameHeight)
		                {
		                    rtcLib.RemoteVideoLib.setVideoText("H264", frames_sec, 8*bytes_sec/1024, rtcLib.frameWidth, rtcLib.frameHeight);
		                }

		            });
		        }
		    }
            }
		}, 1000);
	}

    //初始化媒体
    if (navigator.mediaDevices)
    {
        //尝试获取本地媒体
        navigator.mediaDevices.enumerateDevices()
        .then(function(devices)
        {
            console.log("enumerateDevices");

            DispatchDevices.checkLocalDevice(devices);
            InitLocalMedia();

            var msgData = new Object;
            msgData.Msg = "MediaInitNotify";
            msgData.Type = 1;
            msgData.Reason = "MediaInit Sucess";

            DispatchCtrlLib.FuncCallback(msgData);

            //监听本地设备更改事件
            navigator.mediaDevices.ondevicechange = function(event)
            {
                console.log("本地设备更新");

                var msg = new Object;
                msg.Msg = "LocalDevUpdateReq";
                DispatchCtrlLib.WsSend(msg);

                DispatchDevices.checkLocalDevice();
            }
        })
        .catch(function(error)
        {
            console.log(error);

            var failmsg = new Object;
            failmsg.Msg = "MediaInitNotify";
            failmsg.Type = 0;
            failmsg.Reason = error;

            DispatchCtrlLib.FuncCallback(failmsg);
        });

    }
	else
	{
		console.log("no navigator.mediaDevices");

		var failmsg = new Object;
		failmsg.Msg = "MediaInitNotify";
		failmsg.Type = 0;
		failmsg.Reason = "browser no authority";

		DispatchCtrlLib.FuncCallback(failmsg);

		return;
	}


    return 0;
}


DispatchRtcLib.OnMsgCallback = function(Msg)
{
    var ret = true;
    switch (Msg.Msg)
    {
        case "WebrtcSdpNotify":
        {
            DispatchRtcLib.SetRemoteSdp(Msg.CallID, Msg.WebrtcSdp, Msg.CallType);
            ret = false;
            break;
        }
        case "VoipCallStatusNotify":
        {
			if (CurrentConference.CallID == Msg.CallID)
			{
				DispatchConference.RecvVoipCallStatus(Msg);
				ret = false;
				break;
			}

			if (Msg.Status > 2)
			{
			    DispatchRtcLib.Stop(Msg.CallID);
			}

            break;
        }
        case "VoipDispatchCallRsp":
        {
            if (Msg.Result == 0&&Msg.CallID!= 0)
			{
				DispatchCtrlLib.VoipHandleIncomingCall(Msg.CallID, 1);


			}
            break;
        }
        case "VoipIncomingCallNotify":
        {
            if (Msg.ConferenceInvite)  //通用版本暂不升级到会议接口    机场放开注释  通用注释掉
            // if (false)
            {
                if (CurrentConference.ConfID) //当前已在会议中
                {
                    ret = false;
                    DispatchCtrlLib.VoipHandleIncomingCall(Msg.CallID, 0); //直接拒接
                }
                else
                {
                    ret = false;

                    CurrentConference.CallID = Msg.CallID;
                    CurrentConference.ConfID = Msg.ExtNum;
                    CurrentConference.Type = Msg.CallType;

                    var msg = new Object;
                    msg.Msg = "ConferenceInviteNotify";
                    msg.ConfID = Msg.ExtNum;
                    msg.Type = Msg.CallType;

                    DispatchCtrlLib.FuncCallback(msg);
                }
            }
            break;
        }
        case "MonitorVideoStatusNotify":
        {
            if (Msg.Status > 2)
            {
                DispatchRtcLib.Stop(Msg.CallID);
            }
            break;
        }
		case "ConferenceWsInviteNotify":
		{
		    ret = false;
			DispatchConference.RecvWsInviteNotify(Msg);
		    break;
		}
        default:
        {
            break;
        }
    }
    return ret;
}





DispatchRtcLib.getRtcLib = function(CallID, ForceNew)
{
    var rtcLib = null;
    for (var i=0; i < DispatchRtcLib_Array.length; i++)
    {
        if (DispatchRtcLib_Array[i] != null &&
            DispatchRtcLib_Array[i].CallID == CallID)
        {
            rtcLib = DispatchRtcLib_Array[i];
            console.log("Get DispatchRtcLib_" + rtcLib.index + ", CallID:" + rtcLib.CallID);
            break;
        }
    }

    if (rtcLib == null && ForceNew)
    {
        rtcLib = new DispatchRtcLib;
        rtcLib.CallID = CallID;

        var index = -1;

        for (var i=0; i < DispatchRtcLib_Array.length; i++)
        {
            if (!DispatchRtcLib_Array[i] == null)
            {
                index = i;
                break;
            }
        }

        if (index != -1)
        {
            rtcLib.index = index;
            DispatchRtcLib_Array[index] = rtcLib;
        }
        else
        {
            rtcLib.index = DispatchRtcLib_Array.length;
            DispatchRtcLib_Array.push(rtcLib);
        }

        console.log("New DispatchRtcLib_" + rtcLib.index + ", CallID:" + rtcLib.CallID);
    }

    return rtcLib;
}

var OnAddStream  = function(event)
{
    var rtcLib = event.currentTarget.rtcLib;
    rtcLib.RemoteStream = event.stream;

    if (rtcLib.CallType == DispatchRtcLib_MoniterVideo)
    {
        //单向视频
        if (rtcLib.RemoteVideoLib)
        {
            rtcLib.RemoteVideoLib.video.srcObject = rtcLib.RemoteStream;

            if (rtcLib.CallType == DispatchRtcLib_MoniterVideo)
            {
                //默认静音
            	//rtcLib.RemoteVideoLib.video.muted = true;
				rtcLib.RemoteVideoLib.video.volume = 0;
            }
        }

    }
    else if (rtcLib.CallType == DispatchRtcLib_VoipAudio ||
			 rtcLib.CallType == DispatchRtcLib_ConfAudio)
    {
        //双向语音
        rtcLib.RemoteAudioLabel = document.createElement('audio');
        rtcLib.RemoteAudioLabel.id="DispatchAudio_" + rtcLib.index;
        rtcLib.RemoteAudioLabel.autoplay = true;
        document.body.appendChild(rtcLib.RemoteAudioLabel);

        rtcLib.RemoteAudioLabel.srcObject = rtcLib.RemoteStream;
    }
    else if (rtcLib.CallType == DispatchRtcLib_VoipVideo)
    {
        //双向视频
        if (rtcLib.RemoteVideoLib)
        {
            rtcLib.RemoteVideoLib.video.srcObject = rtcLib.RemoteStream;
        }
    }
	else if (rtcLib.CallType == DispatchRtcLib_ConfVideoPull)
	{
	    //双向视频
	    if (rtcLib.RemoteVideoLib)
	    {
	        rtcLib.RemoteVideoLib.video.srcObject = rtcLib.RemoteStream;
	    }
	}
}

function isIPv4(ip){
	var exp=/^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/;
	var flag = ip.match(exp);
	if(flag != undefined && flag!=""){
		return true;
	} else {
		return false;
	}
}

DispatchRtcLib.CreateOffer = function(CallID, Type, OnCreateOfferSucess, OnCreateOfferFail) {

    var rtcLib = DispatchRtcLib.getRtcLib(CallID, true);
    rtcLib.CallType = Type;

    var localmedia =
    {
        audio: false,
        video: false,
        autoGainControl: true,
        echoCancellation: true,
        noiseSuppression: true,
    };

    var OnCandidateDone  = function() {

        if (rtcLib.CandidateTimeout)
        {
            clearTimeout(rtcLib.CandidateTimeout);
            rtcLib.CandidateTimeout = null;
        }
        console.log("DispatchRtcLib " + rtcLib.CallID + "OnIceCandidate Done");

        if (Type == DispatchRtcLib_MoniterVideo)
        {
            console.log("compressLocalSdp ");
            console.log(rtcLib.localsdp);
            console.log(changesdp(rtcLib.localsdp, rtcLib.LocalVideoCandidates, rtcLib.LocalAudioCandidates));
            OnCreateOfferSucess(compressLocalSdp(rtcLib.localsdp, rtcLib.LocalVideoCandidates, rtcLib.LocalAudioCandidates));
        }
        else
        {
            OnCreateOfferSucess(changesdp(rtcLib.localsdp, rtcLib.LocalVideoCandidates, rtcLib.LocalAudioCandidates));
        }
    }

    var OnIceCandidate  = function(event) {
    	if (event.candidate &&
    		event.candidate.protocol == "udp" &&
    		isIPv4(event.candidate.address))
    	{
    		var v_m = event.currentTarget.localDescription.sdp.indexOf("m=video");
			var a_m = event.currentTarget.localDescription.sdp.indexOf("m=audio");
			if (v_m == -1)
			{
				a_m = 0;
			}
			else if (a_m == -1)
			{
				v_m = 0;
			}
			else
			{
				if (v_m < a_m)
				{
					v_m = 0;
					a_m = 1;
				}
				else
				{
					v_m = 1;
					a_m = 0;
				}
			}

			if (event.candidate.sdpMid == a_m)
    		{
                var can = event.candidate.candidate.split(" ");
                var candidate_str = "";
                for (var i = 0; i < can.length; i++)
                {
                    var line = can[i];
                    if (line)
                    {
                        if (i == 0)
                        {
                            line = "candidate:1";
                        }

                        if (i == 1)
                        {
                            line = "1";
                        }

                        if (i == 3)
                        {
                            line = "1";
                        }

                        if (line == "generation")
                        {
                            can[i+1] = null;
                            continue;
                        }

                        if (line == "ufrag")
                        {
                            can[i+1] = null;
                            continue;
                        }

                        if (line == "network-id")
                        {
                            can[i+1] = null;
                            continue;
                        }

                        if (line == "network-cost")
                        {
                            can[i+1] = null;
                            continue;
                        }

                        candidate_str += line;
                        candidate_str += " ";

                    }
                }
                candidate_str = candidate_str.substr(0, candidate_str.length-1)

                var a = "a=" + candidate_str;
    			rtcLib.LocalAudioCandidates.push(a);

    			console.log("DispatchRtcLib " + rtcLib.CallID + "OnIceCandidate Audio  " + a);
    		}
    		else if (event.candidate.sdpMid == v_m)
    		{
                var can = event.candidate.candidate.split(" ");
                var candidate_str = "";
                for (var i = 0; i < can.length; i++)
                {
                    var line = can[i];
                    if (line)
                    {
                        if (i == 0)
                        {
                            line = "candidate:1";
                        }

                        if (i == 1)
                        {
                            line = "1";
                        }

                        if (i == 3)
                        {
                            line = "1";
                        }

                        if (line == "generation")
                        {
                            can[i+1] = null;
                            continue;
                        }

                        if (line == "ufrag")
                        {
                            can[i+1] = null;
                            continue;
                        }

                        if (line == "network-id")
                        {
                            can[i+1] = null;
                            continue;
                        }

                        if (line == "network-cost")
                        {
                            can[i+1] = null;
                            continue;
                        }

                        candidate_str += line;
                        candidate_str += " ";

                    }
                }
                candidate_str = candidate_str.substr(0, candidate_str.length-1)

                var a = "a=" + candidate_str;
    			rtcLib.LocalVideoCandidates.push(a);

    			console.log("DispatchRtcLib " + rtcLib.CallID + "OnIceCandidate Video  " + a);
    		}
    	}
		else if (event.candidate && event.candidate.protocol == undefined && event.candidate.address == undefined)
		{
			if (event.candidate.sdpMid == "audio")
			{
				var a = "a=" + event.candidate.candidate;
				rtcLib.LocalAudioCandidates.push(a);

				console.log("DispatchRtcLib " + rtcLib.CallID + "OnIceCandidate Audio  " + a);
			}
			else if (event.candidate.sdpMid == "video")
			{
				var a = "a=" + event.candidate.candidate;
				rtcLib.LocalVideoCandidates.push(a);

				console.log("DispatchRtcLib " + rtcLib.CallID + "OnIceCandidate Video  " + a);
			}

			console.log("DispatchRtcLib " + rtcLib.CallID + "OnIceCandidate Video  " + a);
		}
    	else if (event.candidate == null)
    	{
    		OnCandidateDone();
    	}
    }

    rtcLib.peerconnection = new RTCPeerConnection();
    rtcLib.peerconnection.rtcLib = rtcLib;
    rtcLib.peerconnection.onicecandidate = OnIceCandidate;
    rtcLib.peerconnection.onaddstream = OnAddStream;

    var OnGetUserMediaSucess = function(stream)
    {
        var constraints = null;

		if (rtcLib.CallType == DispatchRtcLib_MoniterVideo)
        {
        	//单向视频
            rtcLib.LocalVideoStream = null;
            rtcLib.LocalAudioStream = null;

            rtcLib.peerconnection.addTransceiver("audio", {direction:"recvonly"});
            rtcLib.peerconnection.addTransceiver("video", {direction:"recvonly"});
        }
        else if (rtcLib.CallType == DispatchRtcLib_VoipAudio ||
				 rtcLib.CallType == DispatchRtcLib_ConfAudio)
        {
        	//双向语音
            rtcLib.LocalVideoStream = null;
            rtcLib.LocalAudioStream = stream;

            if (rtcLib.peerconnection.addTrack)
            {
                for (var i = 0; i < stream.getTracks().length; i++)
                {
                    var Track = stream.getTracks()[i];
                    if (Track.kind == "audio")
                    {
                        rtcLib.AudioRTCRtpSender = rtcLib.peerconnection.addTrack(Track, stream);
                        rtcLib.LocalAudioStream = stream;
                        rtcLib.CurrentMicphone = Track.label;
                        console.log("AudioDev Using " + Track.label);
                    }
                }
            }
            else
            {
                rtcLib.peerconnection.addStream(stream);
            }
        }
        else if (rtcLib.CallType == DispatchRtcLib_VoipVideo)
        {
            //双向视频
			if (rtcLib.peerconnection.addTrack)
			{
				for (var i = 0; i < stream.getTracks().length; i++)
				{
					var Track = stream.getTracks()[i];
					if (Track.kind == "audio")
					{
						rtcLib.AudioRTCRtpSender = rtcLib.peerconnection.addTrack(Track, stream);
						rtcLib.LocalAudioStream = stream;
						rtcLib.CurrentMicphone = Track.label;
						console.log("AudioDev Using " + Track.label);
					}
					else if (Track.kind == "video")
					{
						rtcLib.VideoRTCRtpSender = rtcLib.peerconnection.addTrack(Track, stream);
						rtcLib.LocalVideoStream = stream;
						rtcLib.CurrentCaptureDev = Track.label;
						console.log("VideoDev Using " + Track.label);
					}

				}

				if (!localmedia.video)
				{
					//无本地摄像头，使用头像代替
					var Track = DispatchRtcLib.LocalPhotoStream.getVideoTracks()[0];
					console.log("peerconnection.addTrack " + Track.kind);
					rtcLib.VideoRTCRtpSender = rtcLib.peerconnection.addTrack(Track, DispatchRtcLib.LocalPhotoStream);

					if (rtcLib.LocalVideoLib)
					{
						rtcLib.LocalVideoLib.video.srcObject = DispatchRtcLib.LocalPhotoStream;
					}
				}
				else
				{
					if (rtcLib.LocalVideoLib)
					{
						rtcLib.LocalVideoLib.video.srcObject = rtcLib.LocalVideoStream;
						rtcLib.LocalVideoLib.video.muted = true;
					}

				}
			}
			else
			{
				rtcLib.LocalVideoStream = stream;
				rtcLib.peerconnection.addStream(stream);
				if (rtcLib.LocalVideoLib)
				{
					rtcLib.LocalVideoLib.video.srcObject = rtcLib.LocalVideoStream;
					rtcLib.LocalVideoLib.video.muted = true;
				}
			}

        }
		else if (rtcLib.CallType == DispatchRtcLib_ConfVideoPush)
		{
		    //会议视频推送
			for (var i = 0; i < stream.getTracks().length; i++)
			{
				var Track = stream.getTracks()[i];
				if (Track.kind == "video")
				{
					rtcLib.VideoRTCRtpSender = rtcLib.peerconnection.addTrack(Track, stream);
					rtcLib.LocalVideoStream = stream;
					rtcLib.CurrentCaptureDev = Track.label;
					console.log("VideoDev Using " + Track.label);
				}

			}
            
            if (rtcLib.LocalVideoLib)
            {
                rtcLib.LocalVideoLib.video.srcObject = rtcLib.LocalVideoStream;
                rtcLib.LocalVideoLib.video.muted = true;
            }
 

			constraints= {
							offerToReceiveVideo:false,
							offerToReceiveAudio:false
			             };
		}
		else if (rtcLib.CallType == DispatchRtcLib_ConfVideoPull)
		{
			rtcLib.LocalVideoStream = null;
			rtcLib.LocalAudioStream = null;

			//rtcLib.peerconnection.addTransceiver("video", {direction:"recvonly"});
			rtcLib.peerconnection.addTransceiver("video", {direction:"recvonly"});
		}

    	var OnPcCreateOfferSucess = function(offer)
    	{
    		rtcLib.localsdp = offer.sdp;

            var OnSetLocalDescriptionSucess = function()
            {
                console.log("OnSetLocalDescriptionSucess");
            }

            var OnSetLocalDescriptionFail = function(exception)
            {
                console.log("OnSetLocalDescriptionFail " + exception);
                OnCreateOfferFail("初始化本地媒体失败");
            }

    		rtcLib.peerconnection.setLocalDescription(offer, OnSetLocalDescriptionSucess, OnSetLocalDescriptionFail);
            rtcLib.CandidateTimeout = setTimeout(OnCandidateDone, 1000);
    	}

    	var OnPcCreateOfferFail = function(error)
    	{
    		console.log("OnCreateOfferFail " + error);
    		OnCreateOfferFail("初始化本地媒体失败");
    	}

    	rtcLib.peerconnection.createOffer(OnPcCreateOfferSucess, OnPcCreateOfferFail, constraints);
    }

    var OnGetUserMediaFail = function(error)
    {
    	console.log("打开本地设备失败 " + error.code + " : "+ error.message);
        OnCreateOfferFail("打开本地设备失败");
    }


    if (rtcLib.CallType == DispatchRtcLib_MoniterVideo)
    {
    	//单向视频
        OnGetUserMediaSucess(null);
        return CallID;
    }
    else if (rtcLib.CallType == DispatchRtcLib_VoipAudio ||
			 rtcLib.CallType == DispatchRtcLib_ConfAudio)
    {
    	//双向语音
		if (navigator.mediaDevices)
		{
			if (DispatchDevices.SpeakerList.length == 0)
			{
			    //无麦克风或扬声器
			    console.log("本地无可用扬声器");
			    DispatchRtcLib_Array[rtcLib.index] = null;
			    rtcLib = null;
			    return -1;
			}

			if (DispatchDevices.MicphoneList.length == 0)
			{
			    //无麦克风或扬声器
			    console.log("本地无可用麦克风");
			    DispatchRtcLib_Array[rtcLib.index] = null;
			    rtcLib = null;
			    return -2;
			}
		}

        localmedia.audio = {deviceId: DispatchDevices.UsingMicphone.deviceId};
    }
    else if (rtcLib.CallType == DispatchRtcLib_VoipVideo)
    {
    	//双向视频
		if (navigator.mediaDevices)
		{
			if (DispatchDevices.SpeakerList.length == 0)
			{
				//无麦克风或扬声器
				console.log("本地无可用扬声器");
				DispatchRtcLib_Array[rtcLib.index] = null;
				rtcLib = null;
				return -1;
			}

			if (DispatchDevices.MicphoneList.length == 0)
			{
				//无麦克风或扬声器
				console.log("本地无可用麦克风");
				DispatchRtcLib_Array[rtcLib.index] = null;
				rtcLib = null;
				return -2;
			}

			localmedia.audio = {deviceId: DispatchDevices.UsingMicphone.deviceId};

			if (DispatchDevices.CameraList.length > 0)
			{
				//localmedia.video = true;
				localmedia.video = { width: 640, height: 480, deviceId: DispatchDevices.UsingCamera.deviceId};
			}
		}
		else
		{
			localmedia.audio = true;
			localmedia.video = true;
		}
    }
	else if (rtcLib.CallType == DispatchRtcLib_ConfVideoPush)
	{
		//视频推送
		if (navigator.mediaDevices)
		{
			if (DispatchDevices.CameraList.length > 0)
			{
				//localmedia.video = true;
				localmedia.video = { width: 640, height: 480, deviceId: DispatchDevices.UsingCamera.deviceId};
			}
            else
            {
                OnGetUserMediaSucess(DispatchRtcLib.LocalPhotoStream);
                return CallID;
            }
        }
		else
		{
			localmedia.audio = false;
			localmedia.video = true;
		}
	}
	if (rtcLib.CallType == DispatchRtcLib_ConfVideoPull)
	{
		//单向视频
	    OnGetUserMediaSucess(null);
	    return CallID;
	}

    getUserMedia(localmedia, OnGetUserMediaSucess, OnGetUserMediaFail);
    console.log("尝试打开本地设备 " + localmedia.toString());

    return CallID;
}

DispatchRtcLib.SetRemoteSdp = function(CallID, RemoteSdp, CallType)
{
    var rtcLib = DispatchRtcLib.getRtcLib(CallID, true);

    rtcLib.RemoteSdp = RemoteSdp;
    rtcLib.CallType = CallType;

    if (rtcLib.CallType == DispatchRtcLib_MoniterVideo)
    {
        console.log("decompressRemoteSdp ");
        console.log(rtcLib.RemoteSdp);
        rtcLib.RemoteSdp = decompressRemoteSdp(rtcLib.RemoteSdp);
        console.log(rtcLib.RemoteSdp);
    }

    if (rtcLib.peerconnection)
    {
        var remotesdp = new RTCSessionDescription();
        remotesdp.type = "answer";
        
        /* 由服务器处理udp or tcp
        if (rtcLib.CallType == DispatchRtcLib_MoniterVideo &&
            DispatchRtc_UseTcp)
        {
            var sdp_str = "";

            sdp = rtcLib.RemoteSdp.split("\r\n");
            for (var i = 0; i < sdp.length; i++)
            {
                var line = sdp[i];
                if (line.indexOf("a=candidate") != -1 &&
                    line.indexOf("UDP") != -1)
                {
                    continue;
                }

                sdp_str += line;
                sdp_str += "\r\n";
            }
            remotesdp.sdp = sdp_str.substr(0, sdp_str.length-2)
        }
        */
       
        remotesdp.sdp = rtcLib.RemoteSdp;
        
        var OnSetRemoteDescriptionSucess = function()
        {
            console.log("OnSetRemoteDescriptionSucess");
        }

        var OnSetRemoteDescriptionFail = function(exception)
        {
            console.log("OnSetRemoteDescriptionFail " + exception);

            if (rtcLib.CallType == DispatchRtcLib_MoniterVideo)
            {
            	//单向视频
                var failmsg = new Object;
                failmsg.Msg = "MonitorVideoStatusNotify";
                failmsg.CallID = CallID;
                failmsg.Status = 7;
                failmsg.Reason = exception.toString();

                DispatchCtrlLib.FuncCallback(failmsg);

                DispatchCtrlLib.MonitorVideoHangup(CallID);

                DispatchRtcLib.Stop(CallID);
            }
            else
            {
            	//双向语音 双向视频
                var failmsg = new Object;
                failmsg.Msg = "VoipCallStatusNotify";
                failmsg.CallID = CallID;
                failmsg.Status = 7;
                failmsg.Reason = exception.toString();

                DispatchCtrlLib.FuncCallback(failmsg);

                DispatchCtrlLib.VoipHangupCall(CallID);

                DispatchRtcLib.Stop(CallID);
            }
        }

		rtcLib.peerconnection.setRemoteDescription(remotesdp, OnSetRemoteDescriptionSucess, OnSetRemoteDescriptionFail);
	}
}

DispatchRtcLib.CreateAnswer = function(CallID, OnCreateAnswerSucess, OnCreateAnswerFail)
{
    var rtcLib = DispatchRtcLib.getRtcLib(CallID);

    if (rtcLib == null ||
        rtcLib.RemoteSdp == null)
    {
        console.log("初始化本地媒体失败");
        return -3;
    }

    var OnCandidateDone  = function() {

        if (rtcLib.CandidateTimeout)
        {
            clearTimeout(rtcLib.CandidateTimeout);
            rtcLib.CandidateTimeout = null;
        }
        console.log("DispatchRtcLib " + rtcLib.CallID + "OnIceCandidate Done");
        OnCreateAnswerSucess(changesdp(rtcLib.localsdp, rtcLib.LocalVideoCandidates, rtcLib.LocalAudioCandidates));
    }

    var OnIceCandidate = function(event)
    {
    	if (event.candidate &&
    		event.candidate.protocol == "udp" &&
    		isIPv4(event.candidate.address))
    	{
    		var v_m = event.currentTarget.localDescription.sdp.indexOf("m=video");
    		var a_m = event.currentTarget.localDescription.sdp.indexOf("m=audio");
    		if (v_m == -1)
    		{
    			a_m = 0;
    		}
    		else if (a_m == -1)
    		{
    			v_m = 0;
    		}
    		else
    		{
    			if (v_m < a_m)
    			{
    				v_m = 0;
    				a_m = 1;
    			}
    			else
    			{
    				v_m = 1;
    				a_m = 0;
    			}
    		}

			if (event.candidate.sdpMid == a_m)
    		{
    			var a = "a=" + event.candidate.candidate;
    			rtcLib.LocalAudioCandidates.push(a);

    			console.log("DispatchRtcLib " + rtcLib.CallID + "OnIceCandidate Audio  " + a);
    		}
    		else if (event.candidate.sdpMid == v_m)
    		{
    			var a = "a=" + event.candidate.candidate;
    			rtcLib.LocalVideoCandidates.push(a);

    			console.log("DispatchRtcLib " + rtcLib.CallID + "OnIceCandidate Video  " + a);
    		}

    	}
    	else if (event.candidate == null)
    	{
    		OnCandidateDone();
    	}
    }

    rtcLib.peerconnection = new RTCPeerConnection();
    rtcLib.peerconnection.rtcLib = rtcLib;
    rtcLib.peerconnection.onicecandidate = OnIceCandidate;
    rtcLib.peerconnection.onaddstream = OnAddStream;

    var localmedia =
    {
        audio: false,
        video: false,
        autoGainControl: true,
        echoCancellation: true,
        noiseSuppression: true,
    };

    var OnGetUserMediaSucess = function(stream)
    {
        if (rtcLib.CallType == DispatchRtcLib_MoniterVideo)
        {
        	//单向视频
            rtcLib.LocalVideoStream = null;
            rtcLib.LocalAudioStream = null;

            rtcLib.peerconnection.addTransceiver("audio", {direction:"recvonly"});
            rtcLib.peerconnection.addTransceiver("video", {direction:"recvonly"});
        }
        else if (rtcLib.CallType == DispatchRtcLib_VoipAudio ||
				 rtcLib.CallType == DispatchRtcLib_ConfAudio)
        {
        	//双向语音
            rtcLib.LocalVideoStream = null;
            rtcLib.LocalAudioStream = stream;

            for (const Track of stream.getTracks())
            {
                if (Track.kind == "audio")
                {
                    rtcLib.AudioRTCRtpSender = rtcLib.peerconnection.addTrack(Track, stream);
                    rtcLib.LocalAudioStream = stream;
                    rtcLib.CurrentMicphone = Track.label;
                    console.log("AudioDev Using " + Track.label);
                }
            }
        }
        else if (rtcLib.CallType == DispatchRtcLib_VoipVideo)
        {
            //双向视频
            for (const Track of stream.getTracks())
            {
                if (Track.kind == "audio")
                {
                    rtcLib.AudioRTCRtpSender = rtcLib.peerconnection.addTrack(Track, stream);
                    rtcLib.LocalAudioStream = stream;
					rtcLib.CurrentMicphone = Track.label;
					console.log("AudioDev Using " + Track.label);
                }
                else if (Track.kind == "video")
                {
                    rtcLib.VideoRTCRtpSender = rtcLib.peerconnection.addTrack(Track, stream);
                    rtcLib.LocalVideoStream = stream;
                    rtcLib.CurrentCaptureDev = Track.label;
                    console.log("VideoDev Using " + Track.label);
                }

            }

            if (!localmedia.video)
            {
                //无本地摄像头，使用头像代替
				var Track = DispatchRtcLib.LocalPhotoStream.getVideoTracks()[0];
				console.log("peerconnection.addTrack " + Track.kind);
				rtcLib.VideoRTCRtpSender = rtcLib.peerconnection.addTrack(Track, DispatchRtcLib.LocalPhotoStream);

                if (rtcLib.LocalVideoLib)
                {
                    rtcLib.LocalVideoLib.video.srcObject = DispatchRtcLib.LocalPhotoStream;
                }
            }
            else
            {
                if (rtcLib.LocalVideoLib)
                {
                    rtcLib.LocalVideoLib.video.srcObject = rtcLib.LocalVideoStream;
					rtcLib.LocalVideoLib.video.muted = true;
                }
            }

        }

    	var OnSetRemoteDescriptionSucess = function(offer)
    	{
            var OnPcCreateAnswerSucess = function(answer)
            {
                console.log("OnPcCreateAnswerSucess");
                rtcLib.localsdp = answer.sdp;

                var OnSetLocalDescriptionSucess = function()
                {
                    console.log("OnSetLocalDescriptionSucess");
                }

                var OnSetLocalDescriptionFail = function(exception)
                {
                    console.log("OnSetLocalDescriptionFail " + exception.toString());
                    OnCreateAnswerFail(exception.toString());
                }

                console.log(answer.sdp);
                rtcLib.peerconnection.setLocalDescription(answer, OnSetLocalDescriptionSucess, OnSetLocalDescriptionFail);
                rtcLib.CandidateTimeout = setTimeout(OnCandidateDone, 1000);
            }

            var OnPcCreateAnswerFail = function(exception)
            {
                console.log("OnPcCreateAnswerFail " + exception.toString());
                OnCreateAnswerFail(exception.toString());
            }

            rtcLib.peerconnection.createAnswer(OnPcCreateAnswerSucess, OnPcCreateAnswerFail);
    	}

    	var OnSetRemoteDescriptionFail = function(error)
    	{
    		console.log("OnSetRemoteDescriptionFail " + error.toString());
    		OnCreateAnswerFail(error.toString());
    	}

    	rtcLib.RemoteSdp = changesdp_local(rtcLib.RemoteSdp);

    	var remotesdp = new RTCSessionDescription();
    	remotesdp.type = "offer";
    	remotesdp.sdp = rtcLib.RemoteSdp;

    	rtcLib.peerconnection.setRemoteDescription(remotesdp, OnSetRemoteDescriptionSucess, OnSetRemoteDescriptionFail);
    }

    var OnGetUserMediaFail = function(error)
    {
    	console.log("打开本地设备失败 " + error.code + " : "+ error.message);
        OnCreateAnswerFail("打开本地设备失败");
    }

    if (rtcLib.CallType == DispatchRtcLib_MoniterVideo)
    {
    	//单向视频
        OnGetUserMediaSucess(null);
        return 0;
    }
    else if (rtcLib.CallType == DispatchRtcLib_VoipAudio ||
			 rtcLib.CallType == DispatchRtcLib_ConfAudio)
    {
    	//双向语音
        if (DispatchDevices.SpeakerList.length == 0)
        {
            //无麦克风或扬声器
            console.log("本地无可用扬声器");
            DispatchRtcLib_Array[rtcLib.index] = null;
            rtcLib = null;
            return -1;
        }

        if (DispatchDevices.MicphoneList.length == 0)
        {
            //无麦克风或扬声器
            console.log("本地无可用麦克风");
            DispatchRtcLib_Array[rtcLib.index] = null;
            rtcLib = null;
            return -2;
        }

        localmedia.audio = {deviceId: DispatchDevices.UsingMicphone.deviceId};
    }
    else if (rtcLib.CallType == DispatchRtcLib_VoipVideo)
    {
    	//双向视频
        if (DispatchDevices.SpeakerList.length == 0)
        {
            //无麦克风或扬声器
            console.log("本地无可用扬声器");
            DispatchRtcLib_Array[rtcLib.index] = null;
            rtcLib = null;
            return -1;
        }

        if (DispatchDevices.MicphoneList.length == 0)
        {
            //无麦克风或扬声器
            console.log("本地无可用麦克风");
            DispatchRtcLib_Array[rtcLib.index] = null;
            rtcLib = null;
            return -2;
        }

        localmedia.audio = {deviceId: DispatchDevices.UsingMicphone.deviceId};

        if (DispatchDevices.CameraList.length > 0)
        {
            //localmedia.video = true;
            localmedia.video = { width: 640, height: 480, deviceId: DispatchDevices.UsingCamera.deviceId};
        }
    }

    getUserMedia(localmedia, OnGetUserMediaSucess, OnGetUserMediaFail);
}

DispatchRtcLib.DisplayVideo = function(CallID, VideoLib)
{
    if (CallID == -1)
    {
        for (var i=0; i < DispatchRtcLib_Array.length; i++)
        {
            if (DispatchRtcLib_Array[i] != null &&
               (DispatchRtcLib_Array[i].CallType == DispatchRtcLib_VoipVideo ||
				DispatchRtcLib_Array[i].CallType == DispatchRtcLib_ConfVideoPush))
            {
                DispatchRtcLib_Array[i].LocalVideoLib = VideoLib;

				if (DispatchRtcLib_Array[i].LocalVideoStream)
				{
					VideoLib.video.srcObject = DispatchRtcLib_Array[i].LocalVideoStream;
					VideoLib.video.muted = true;
				}
				else
				{
					VideoLib.video.srcObject = DispatchRtcLib.LocalPhotoStream;
				}

                break;
            }
        }
    }
    else
    {
        var rtcLib = DispatchRtcLib.getRtcLib(CallID);

        rtcLib.RemoteVideoLib = VideoLib;

        if (rtcLib.RemoteStream)
        {
            VideoLib.video.srcObject = rtcLib.RemoteStream;

			if (rtcLib.CallType == DispatchRtcLib_MoniterVideo)
			{
			    //默认静音
				//VideoLib.video.muted = true;
				VideoLib.video.volume = 0;
			}
        }
    }
}

DispatchRtcLib.Stop = function(CallID)
{
    var rtcLib = DispatchRtcLib.getRtcLib(CallID);
    if (rtcLib == null)
    {
        return;
    }

    if (rtcLib.RemoteStream)
    {
		for (var i = 0; i < rtcLib.RemoteStream.getTracks().length; i++)
		{
		    rtcLib.RemoteStream.getTracks()[i].stop();
		}

        rtcLib.RemoteStream = null;
    }

    if (rtcLib.LocalVideoStream &&
	    !rtcLib.LocalVideoStream.bLocalPhoto)
    {
        for (var i = 0; i < rtcLib.LocalVideoStream.getTracks().length; i++)
        {
            rtcLib.LocalVideoStream.getTracks()[i].stop();
		}

        rtcLib.LocalVideoStream = null;
    }

	if (rtcLib.LocalAudioStream)
	{
	    for (var i = 0; i < rtcLib.LocalAudioStream.getTracks().length; i++)
	    {
	        rtcLib.LocalAudioStream.getTracks()[i].stop();
	    }

	    rtcLib.LocalAudioStream = null;
	}

    if (rtcLib.LocalVideoLib)
    {
        rtcLib.LocalVideoLib.StopDisplay();
		rtcLib.LocalVideoLib.video.muted = false;
        rtcLib.LocalVideoLib = null;
    }

    if (rtcLib.RemoteVideoLib)
    {
        rtcLib.RemoteVideoLib.StopDisplay();
		rtcLib.RemoteVideoLib.video.muted = false;
        rtcLib.RemoteVideoLib = null;
    }

    if (rtcLib.RemoteAudioLabel)
    {
        document.body.removeChild(rtcLib.RemoteAudioLabel);
        rtcLib.RemoteAudioLabel = null;
    }

    if (rtcLib.peerconnection)
    {
        rtcLib.peerconnection.close();
        rtcLib.peerconnection = null;
    }


    console.log("DispatchRtcLib.Stop " + rtcLib.index + " CallID: " + CallID);
    DispatchRtcLib_Array[rtcLib.index] = null;
    rtcLib = null;
}

DispatchRtcLib.VoipSetCamera = function(CallID, VideoSrc)
{
    var rtcLib = DispatchRtcLib.getRtcLib(CallID);
    if (rtcLib == null ||
        (rtcLib.CallType != DispatchRtcLib_VoipVideo && rtcLib.CallType != DispatchRtcLib_ConfVideoPush))
    {
        return -1;
    }

	if (rtcLib.CurrentCaptureDev == VideoSrc)
	{
	    return 0;
	}

    var old_track = rtcLib.VideoRTCRtpSender.track;
    
    var replaceVideoTrack = function(stream)
    {
        rtcLib.VideoRTCRtpSender.replaceTrack(stream.getVideoTracks()[0])
        .then(() =>
        {
            console.log("replaceTrack Sucess to " + VideoSrc);
            
            if (rtcLib.CurrentCaptureDev == "screen")
            {               
                if (rtcLib.CallType == DispatchRtcLib_ConfVideoPush)
                {
                    var xml = 
                    "<VOIP>" +
                    "<Command-Name>ConferenceScreenShareEnd</Command-Name>" +
                    "<Command-Arg>"+CurrentConference.ConfID+"</Command-Arg>" +
                    "<Command-ID></Command-ID>" +
                    "<Command-Info>" +
                        "<ExtNum>" + DispatchCtrlLib.extnumber + "</ExtNum>" +
                    "</Command-Info>" +
                    "</VOIP>\r\n";
                    
                    DispatchConference.WsSend(xml);
                }
                //防止OnDisplayMediaInactive
                rtcLib.LocalVideoStream.ForbiddenInactive = true;
            }
            else if (VideoSrc == "screen")
            {
                if (rtcLib.CallType == DispatchRtcLib_ConfVideoPush)
                {
                    var xml = 
                    "<VOIP>" +
                    "<Command-Name>ConferenceScreenShareStart</Command-Name>" +
                    "<Command-Arg>"+CurrentConference.ConfID+"</Command-Arg>" +
                    "<Command-ID></Command-ID>" +
                    "<Command-Info>" +
                        "<ExtNum>" + DispatchCtrlLib.extnumber + "</ExtNum>" +
                    "</Command-Info>" +
                    "</VOIP>\r\n";
                    
                    DispatchConference.WsSend(xml);
                }
            }
            
            rtcLib.CurrentCaptureDev = VideoSrc;
            
            if (rtcLib.LocalVideoLib)
            {
                rtcLib.LocalVideoLib.video.srcObject = stream;
                rtcLib.LocalVideoLib.video.muted = true;
            }
            rtcLib.LocalVideoStream = stream;
            if (!old_track.bLocalPhoto)
            {
                old_track.stop();
            }
        })
        .catch(error => 
        {
            console.log(error);
        });
    }
    
    
    if (VideoSrc == "screen")
    {		        
        if (rtcLib.CallType == DispatchRtcLib_ConfVideoPush)
        {
            //屏幕共享时,判断会议中是否有人屏幕共享中
            if (CurrentConference.ScreenShare.length != 0)
            {
                console.log("会议中有其他人正在桌面共享 " + CurrentConference.ScreenShare);
                return -5;
            }
        }
        
        var OnGetDisplayMediaSucess = function(stream)
        {
            var OnDisplayMediaInactive = function(event)
            {
                console.log("停止桌面共享");
                
                if (rtcLib.LocalVideoStream.ForbiddenInactive)
                {
                    return;
                }
                if (DispatchDevices.CameraList.length == 0)
                {
                    setTimeout(DispatchRtcLib.VoipSetCamera(CallID, "none"), 1000);
                }
                else
                {
                    setTimeout(DispatchRtcLib.VoipSetCamera(CallID, DispatchDevices.CameraList[0].label), 1000);
                }
            }

            // stream.addEventListener("inactive", OnDisplayMediaInactive);

            replaceVideoTrack(stream);
        }

        navigator.mediaDevices.getDisplayMedia({video:true})
        //navigator.mediaDevices.getDisplayMedia({video:{ width: 640, height: 480}})
        .then(stream =>
        {
            OnGetDisplayMediaSucess(stream);
        })
        .catch(error =>
        {
            console.log(error);
        });
    }
    else if (VideoSrc == "none")
    {
        replaceVideoTrack(DispatchRtcLib.LocalPhotoStream);
    }
    else
	{
	    var device = null;
		for (var i = 0; i < DispatchDevices.CameraList.length; i++)
	    {
	        var camera = DispatchDevices.CameraList[i];
			if (camera.label == VideoSrc)
			{
				device = camera.deviceId;
				break;
			}
		}

		if (device)
		{
	        var localmedia =
	        {
	            audio: false,
	            video: false,
	            autoGainControl: true,
	            echoCancellation: true,
	            noiseSuppression: true,
	        };

			localmedia.video = { width: 640, height: 480, deviceId: device};

			var OnGetUserMediaSucess = function(stream)
			{
                replaceVideoTrack(stream);
            }
            
            var OnGetUserMediaFail = function(error)
			{
				console.log("打开本地设备失败 " + error.code + " : "+ error.message);
			}

            getUserMedia(localmedia, OnGetUserMediaSucess, OnGetUserMediaFail);
        }
        else
        {
            return -3;
        }
    }

    return 0;
}

DispatchRtcLib.VoipSetMicphone = function(CallID, Micphone)
{
    var device = null;
	  var groupId = null;
	  var old_groupId = null;
    var rtcLib = DispatchRtcLib.getRtcLib(CallID);
    for (var i = 0; i < DispatchDevices.MicphoneList.length; i++)
    {
        if (DispatchDevices.MicphoneList[i].label == Micphone)
        {
            console.log("VoipSetMicphone to " + DispatchDevices.MicphoneList[i].label);
            device = DispatchDevices.MicphoneList[i].deviceId;
			groupId = DispatchDevices.MicphoneList[i].groupId;
        }

		if (DispatchDevices.MicphoneList[i].label == rtcLib.CurrentMicphone)
		{
			old_groupId = DispatchDevices.MicphoneList[i].groupId;
		}
    }

    if (!device)
    {
        return -1;
    }


    if (rtcLib.CurrentMicphone == Micphone ||
		groupId == old_groupId)
    {
        console.log("VoipSetMicphone nochange ");
		return 0;
    }

    var old_track = rtcLib.AudioRTCRtpSender.track;

    var localmedia =
    {
        audio: {deviceId: device},
        video: false,
        autoGainControl: true,
        echoCancellation: true,
        noiseSuppression: true,
    };

	var OnGetUserMediaSucess = function(stream)
	{
		rtcLib.AudioRTCRtpSender.replaceTrack(stream.getAudioTracks()[0])
		.then(() =>
		{
			console.log("replaceTrack Sucess to " + Micphone);
			rtcLib.CurrentMicphone = Micphone;

			old_track.stop();
		})
		.catch(error =>
		{
			console.log(error);
		});
	}

	var OnGetUserMediaFail = function(error)
	{
		console.log("打开本地设备失败 " + error.code + " : "+ error.message);
	}

	getUserMedia(localmedia, OnGetUserMediaSucess, OnGetUserMediaFail);
}

DispatchRtcLib.VoipSetCallVolume = function(CallID, Volume)
{
    var rtcLib = DispatchRtcLib.getRtcLib(CallID);
    if (rtcLib == null)
    {
        return -1;
    }

	if (Volume < 0)
	{
		Volume = 0;
	}
	else if (Volume > 100)
	{
		Volume = 100;
	}

	Volume = Volume / 100.0;

    if (rtcLib.RemoteAudioLabel)
    {
        rtcLib.RemoteAudioLabel.volume = Volume;
    }

    if (rtcLib.RemoteVideoLib)
    {
        rtcLib.RemoteVideoLib.video.volume = Volume;
    }

    console.log("DispatchRtcLib.VoipSetCallVolume " + Volume);

    return 0;
}

DispatchRtcLib.VoipSetCallMicMute = function(CallID, Mute)
{
	var rtcLib = DispatchRtcLib.getRtcLib(CallID);
	if (rtcLib == null)
	{
	    return -1;
	}

	var bEnabled = true;
	if (Mute)
	{
		bEnabled = false;
	}

	if (rtcLib.LocalAudioStream)
	{
	    var AudioTracks = rtcLib.LocalAudioStream.getAudioTracks();
		for (var i = 0; i < AudioTracks.length; i++)
	    {
			AudioTracks[i].enabled = bEnabled;
	    }
	}

	return 0;
}

DispatchRtcLib.VoipSetSpeaker = function(CallID, Speaker)
{
    var deviceId = null;
    for (var i = 0; i < DispatchDevices.SpeakerList.length; i++)
    {
        if (DispatchDevices.SpeakerList[i].label == Speaker)
        {
            console.log("VoipSetSpeaker to " + DispatchDevices.SpeakerList[i].label);
            deviceId = DispatchDevices.SpeakerList[i].deviceId;
			break;
		}
	}

	if (!deviceId)
	{
		return -3;
	}

	var rtcLib = DispatchRtcLib.getRtcLib(CallID);
	if (rtcLib == null)
	{
	    return -1;
	}

    if (rtcLib.CallType == DispatchRtcLib_MoniterVideo ||
		rtcLib.CallType == DispatchRtcLib_VoipVideo)
    {
    	//视频  rtcLib
		rtcLib.RemoteVideoLib.video.setSinkId(deviceId)
			.then(function()
			{
				console.log("VoipSetSpeaker Sucess");
			})
			.catch(function(error)
			{
				console.log("VoipSetSpeaker Fail");
				console.log(error)
			});
    }
    else if (rtcLib.CallType == DispatchRtcLib_VoipAudio ||
			 rtcLib.CallType == DispatchRtcLib_ConfAudio)
    {
		//语音
		rtcLib.RemoteAudioLabel.setSinkId(deviceId)
			.then(function()
			{
				console.log("VoipSetSpeaker Sucess");
			})
			.catch(function(error)
			{
				console.log("VoipSetSpeaker Fail");
				console.log(error)
			});
    }

	return 0;
}

//————————————————————————————————————— DispatchConference ——————————————————————————————————————————
function DispatchConferenceMember() {
	this.Status = 0;  	//状态
	this.ExtNum = "";  	//分机号
	this.MemName = "";  //名称
	this.MemType = "";  //类型 1-调度台 2-手机 3-SIP话机 4-未知分机
	this.MemMute = 0;	//是否禁言  0-讲话中 1-禁言中
	this.MemVideo = 0;	//视频是否就绪  0-未就绪 1-就绪
};

function DispatchConference() {
	this.ConfID = null;
	this.MemberList = new Array;

	this.CallID = 0;		//对应呼叫ID
	this.CallStatus = 0;	//呼叫状态
	this.Type = 0;			//0-语音会议 1-视频会议

	this.websocket = null;

	this.VideoPush_CallID = 0;
};

var CurrentConference = new DispatchConference;

//当前会议信息
DispatchConference.CurrentConferenceInfo = function()
{
    if (CurrentConference.ConfID)
	{
		return CurrentConference;
	}
	else
	{
		return null;
	}
}

DispatchConference.ConferenceEnter = function(ConfID, Type, ConfPwd)
{
  console.log(ScsConstants.VoipCallID);
	if (CurrentConference.ConfID)
	{
		return -2;
	}

	var msg = new Object;
	msg.Msg = "VoipMakeAudioCallReq";
	msg.ExtNum = ConfID;
	if (typeof ConfPwd == "string")
	{
		msg.ConfPwd = ConfPwd;
	}
	msg.CallID = ScsConstants.VoipCallID++;

	var OnMakeCallSucess = function(sdp)
	{
	    msg.OfferSdp = sdp;

	    console.log("DispatchConference.ConferenceEnter " + msg.CallID + " local_sdp:");
	    console.log(sdp);

	    DispatchCtrlLib.WsSend(msg);

		CurrentConference.ConfID = ConfID;
		CurrentConference.CallID = msg.CallID;
		CurrentConference.CallStatus = 1;
		CurrentConference.Type = Type;
	}

	var OnMakeCallFail = function(error)
	{
	    console.log("DispatchConference.ConferenceEnter Fail:" + error);

	    var failmsg = new Object;
	    failmsg.Msg = "ConferenceEnterNotify";
	    failmsg.ConfID = ConfID;
	    failmsg.Status = 7;
	    failmsg.Reason = error;

	    DispatchCtrlLib.FuncCallback(failmsg);
	}

	return DispatchRtcLib.CreateOffer(msg.CallID, DispatchRtcLib_ConfAudio, OnMakeCallSucess, OnMakeCallFail);
}

DispatchConference.RecvVoipCallStatus = function(msg)
{
	var failmsg = new Object;
	failmsg.Msg = "ConferenceEnterNotify";
	failmsg.ConfID = CurrentConference.ConfID;
	failmsg.Status = msg.Status;

	if (msg.Status > 2)
	{
		//会议停止
		DispatchConference.Stop();
	}

	DispatchCtrlLib.FuncCallback(failmsg);
}

DispatchConference.ConferenceExit = function()
{
	if (!CurrentConference.ConfID)
	{
		return -2;
	}

	var msg = new Object;
	msg.Msg = "VoipHangupCallReq";
	msg.CallID = CurrentConference.ConfID;

	DispatchCtrlLib.WsSend(msg);

	DispatchConference.Stop();
}

DispatchConference.WsSend = function(Msg)
{
	if (CurrentConference.websocket)
	{
		console.log("DispatchConference.WsSend: " + Msg);
		CurrentConference.websocket.send(Msg);
	}
}

DispatchConference.RecvWsInviteNotify = function(msg)
{
	if (!CurrentConference.ConfID)
	{
		return -2;
	}

	console.log("new WebSocket " + msg.Url);
	var websocket = new WebSocket(msg.Url);
	websocket.binaryType = 'arraybuffer';
	websocket.onopen = function(evt) {
		console.log("websocket.onopen");
		var xml =
		"<VOIP>" +
		"<Command-Name>ConferenceWsInit</Command-Name>" +
		"<Command-Arg>"+CurrentConference.ConfID+"</Command-Arg>" +
		"<Command-ID></Command-ID>" +
		"<Command-Info>" +
			"<ExtNum>"+DispatchCtrlLib.extnumber+"</ExtNum>" +
		"</Command-Info>" +
		"</VOIP>";
		DispatchConference.WsSend(xml);
	}
	websocket.onclose = function(evt) {
		console.log("websocket.onclose");
		CurrentConference.websocket = null;
	}
	websocket.onerror = function(evt) {
		console.log("websocket.onerror");
	}
	websocket.onmessage = DispatchConference.WebsocketOnMessage;

	CurrentConference.websocket = websocket;
}

DispatchConference.WebsocketOnMessage = function(evt) {

	var getNodeValue = function(xml, name)
	{
		for (var i = 0; i < xml.all.length; i++)
		{
			if (xml.all[i].tagName == name)
			{
				return xml.all[i].textContent;
			}
		}
		return null;
	}

  var getNodeList;
  getNodeList = function(xml, name)
	{
		var list = new Array;
		for (var i = 0; i < xml.all.length; i++)
		{
			if (xml.all[i].tagName == name)
			{
				var obj = {
				};
				for (var j = 0; j < xml.all[i].childNodes.length; j++)
				{
					var tagName = xml.all[i].childNodes[j].tagName;
					var textContent = xml.all[i].childNodes[j].textContent;
					obj[tagName] = textContent;
				}
				list.push(obj);
			}
		}

		return list;
	}

	if (typeof(evt.data) == "string")
	{
	    console.log("DispatchConference.WebsocketOnMessage:  " + evt.data);

		var xml_end = evt.data.indexOf("</VOIP>") + "</VOIP>".length;
		var xml = evt.data.substring(0, xml_end);

		var doc = (new DOMParser()).parseFromString(xml, "text/xml");

		var ConfID = getNodeValue(doc, "Command-Arg");
		if (ConfID != CurrentConference.ConfID)
		{
			console.log("Command-Arg " + ConfID + " != CurrentConference.ConfID " + CurrentConference.ConfID);
			return;
		}

		var CommandName = getNodeValue(doc, "Command-Name");

		switch(CommandName)
		{
			case "ConferenceInfo":
			{
                CurrentConference.HostExtNum = getNodeValue(doc, "HostExtNum");
                CurrentConference.ScreenShare = getNodeValue(doc, "ScreenShare");
                
				var MemList = getNodeList(doc, "Member");

				for (var i = 0; i < MemList.length; i++)
				{
					var Member = {
						ExtNum: MemList[i].ExtNum,
						MemName: MemList[i].MemName,
						MemType: MemList[i].MemType,
						Status: MemList[i].MemStatus,
						MemMute: MemList[i].Mute,
						MemVideo: MemList[i].Video,
						PullCallID: 0
					};

					CurrentConference.MemberList.push(Member);
				}

				var msg = new Object;
				msg.Msg = "ConferenceStatusNotify";
				msg.ConfID = CurrentConference.ConfID;
				msg.Status = -1;

				DispatchCtrlLib.FuncCallback(msg);

				if (CurrentConference.Type)
				{
					//视频会议 开始推送视频
					DispatchConference.VideoPushReq();
				}

				break;
			}
			case "ConferenceStatus":
			{

				var Status = parseInt(getNodeValue(doc, "Status"));
				var ExtNum = getNodeValue(doc, "ExtNum");
				var MemName = getNodeValue(doc, "MemName");
				var MemType = parseInt(getNodeValue(doc, "MemType"));
				var MemStatus = parseInt(getNodeValue(doc, "MemStatus"));
				var Mute = parseInt(getNodeValue(doc, "Mute"));
				var Video = parseInt(getNodeValue(doc, "Video"));

				var Index = -1;
				for (var i = 0 ; i < CurrentConference.MemberList.length; i++)
				{
					if (CurrentConference.MemberList[i].ExtNum == ExtNum)
					{
						Index = i;
						break;
					}
				}

				if (ExtNum&&Index>=0)
				{
					CurrentConference.MemberList[Index].ExtNum = ExtNum;
					CurrentConference.MemberList[Index].MemName = MemName;
					CurrentConference.MemberList[Index].MemType = parseInt(MemType);
					CurrentConference.MemberList[Index].Status = parseInt(MemStatus);
					CurrentConference.MemberList[Index].Mute = parseInt(Mute);
					CurrentConference.MemberList[Index].Video = parseInt(Video);
				}
				/*
					0-成员移除
					1-成员已接入
					2-成员未接入
					3-成员连接中
					4-会议结束
					5-成员禁言
					6-成员禁言取消
					7-成员开始讲话
					8-成员结束讲话
                    9-主持人变更
                    10-视频就绪
                    11-视频结束
                    12-屏幕共享开始
                    13-屏幕共享结束
				*/
				switch(Status)
				{
					case 0:
					{
						//0-成员移除
						if (Index != -1)
						{
							CurrentConference.MemberList.slice(Index);
						}
						break;
					}
					case 1:
					{
						//1-成员已接入
						if (Index == -1)
						{
							var Member = {
								ExtNum: ExtNum,
								MemName: MemName,
								MemType: MemType,
								Status: MemStatus,
								MemMute: Mute,
								MemVideo: Video,
								PullCallID: 0
							};

							CurrentConference.MemberList.push(Member);
						}
						break;
					}
					case 2:
					{
						//2-成员未接入
						if (Index == -1)
						{
							var Member = {
								ExtNum: ExtNum,
								MemName: MemName,
								MemType: MemType,
								Status: MemStatus,
								MemMute: Mute,
								MemVideo: Video,
								PullCallID: 0
							};

							CurrentConference.MemberList.push(Member);
						}
						else
						{
							CurrentConference.MemberList[Index].Status = 2;
						}
						break;
					}
					case 3:
					{
						//3-成员连接中
						if (Index == -1)
						{
							var Member = {
								ExtNum: ExtNum,
								MemName: MemName,
								MemType: MemType,
								Status: MemStatus,
								MemMute: Mute,
								MemVideo: Video,
								PullCallID: 0
							};

							CurrentConference.MemberList.push(Member);
						}
						else
						{
							CurrentConference.MemberList[Index].Status = 3;
						}
						break;
					}
					case 4:
					{
                        //4-会议结束
                        DispatchConference.Stop();
                        break;
                    }
                    case 9:
                    {
                        //9-主持人变更
                        CurrentConference.HostExtNum = ExtNum;
                        break;
                    }
                    case 12:
                    {
                        //12-屏幕共享开始
                        CurrentConference.ScreenShare = ExtNum;
                        
                        break;
                    }
                    case 13:
                    {
                        //13-屏幕共享结束
                        CurrentConference.ScreenShare = "";
                        break;
                    }
					default:
						break;
				}

				var cb_msg = {
					Msg: "ConferenceStatusNotify",
					ConfID: ConfID,
					ExtNum: ExtNum,
					MemName: MemName,
					MemType: MemType,
					Status: parseInt(Status),
					MemMute: Mute,
					MemVideo: Video
				};

				DispatchCtrlLib.FuncCallback(cb_msg);

				break;
			}
			case "ConferenceVideoPushRsp":
			{
				var CommandID = getNodeValue(doc, "Command-ID");
				var Result = getNodeValue(doc, "Result");

				if (Result == 0)
				{
					var sdp_start = evt.data.indexOf("v=0");
					var sdp = evt.data.substring(sdp_start);

					if (evt.data.indexOf("test666") == -1)
					{
						//测试
						DispatchConference.VideoPushRsp(sdp);
					}

					var xml =
					"<VOIP>" +
					"<Command-Name>ConferenceVideoPushAck</Command-Name>" +
					"<Command-Arg>"+CurrentConference.ConfID+"</Command-Arg>" +
					"<Command-ID>"+CurrentConference.ConfID+"-"+DispatchCtrlLib.extnumber+"</Command-ID>" +
					"<Command-Info>" +
						"<ExtNum>" + DispatchCtrlLib.extnumber + "</ExtNum>" +
					"</Command-Info>" +
					"</VOIP>\r\n";

					DispatchConference.WsSend(xml);

				}
				else
				{
					console.log("ConferenceVideoPushRsp Result: " + Result);
					DispatchRtcLib.Stop(CurrentConference.VideoPush_CallID);
					CurrentConference.VideoPush_CallID = 0;
				}

				break;
			}
			case "ConferenceVideoPushEnd":
			{
				if (CurrentConference.VideoPush_CallID)
				{
					DispatchRtcLib.Stop(CurrentConference.VideoPush_CallID);
					CurrentConference.VideoPush_CallID = 0;
				}
				break;
			}
			case "ConferenceVideoPullRsp":
			{
				var CommandID = getNodeValue(doc, "Command-ID");
				var Result = getNodeValue(doc, "Result");
				var Target = getNodeValue(doc, "Target");

				if (Result == 0)
				{
					var sdp_start = evt.data.indexOf("v=0");
					var sdp = evt.data.substring(sdp_start);

					if (evt.data.indexOf("test666") == -1)
					{
						//测试
						for (var i = 0; i < CurrentConference.MemberList.length; i++)
						{
							if (CurrentConference.MemberList[i].ExtNum == Target)
							{
								DispatchConference.VideoPullRsp(CurrentConference.MemberList[i].PullCallID, sdp);
								break;
							}
						}
					}

					var xml =
					"<VOIP>" +
					"<Command-Name>ConferenceVideoPullAck</Command-Name>" +
					"<Command-Arg>"+CurrentConference.ConfID+"</Command-Arg>" +
					"<Command-ID>"+CurrentConference.ConfID+"-"+DispatchCtrlLib.extnumber+"</Command-ID>" +
					"<Command-Info>" +
						"<ExtNum>" + DispatchCtrlLib.extnumber + "</ExtNum>" +
					"</Command-Info>" +
					"</VOIP>\r\n";

					DispatchConference.WsSend(xml);

				}
				else
				{
					console.log("ConferenceVideoPushRsp Result: " + Result);
					DispatchRtcLib.Stop(CurrentConference.VideoPush_CallID);
					CurrentConference.VideoPush_CallID = 0;
				}

				break;
			}
			case "ConferenceVideoPullEnd":
			{
				var CommandID = getNodeValue(doc, "Command-ID");
				var Target = getNodeValue(doc, "Target");

				for (var i = 0; i < CurrentConference.MemberList.length; i++)
				{
					if (CurrentConference.MemberList[i].ExtNum == Target)
					{
						DispatchRtcLib.Stop(CurrentConference.MemberList[i].PullCallID);
						CurrentConference.MemberList[i].PullCallID = 0;
						break;
					}
				}

				break;
			}
			default:
				break;
		}

	}
}

//处理会议邀请
DispatchConference.ConferenceHandleInvite = function(Handle)
{
	if (!CurrentConference.ConfID)
	{
		return -2;
	}

	if (Handle)
	{
		//接听
		DispatchCtrlLib.VoipHandleIncomingCall(CurrentConference.CallID, 1);
	}
	else
	{
		DispatchCtrlLib.VoipHandleIncomingCall(CurrentConference.CallID, 0);
	}
}

//会议停止
DispatchConference.Stop = function()
{
	DispatchRtcLib.Stop(CurrentConference.CallID);

	if (CurrentConference.Type == 1)
	{
		//视频会议

	}

	if (CurrentConference.websocket)
	{
		CurrentConference.websocket.close();
		CurrentConference.websocket = null;
	}

	CurrentConference.CallStatus = 0;
	CurrentConference.ConfID = null;
	CurrentConference.CallID = 0;
	CurrentConference.MemberList = new Array;
}

//离开会议
DispatchConference.ConferenceExit = function()
{
	if (!CurrentConference.ConfID)
	{
		return -2;
	}

	var msg = new Object;
	msg.Msg = "VoipHangupCallReq";
	msg.CallID = CurrentConference.CallID;
	DispatchCtrlLib.WsSend(msg);

	DispatchConference.Stop();

	return 0;
}

//会议邀请
DispatchConference.ConferenceInvite = function(ExtNumList)
{
	if (!CurrentConference.ConfID)
	{
		return -2;
	}

	var xml =
	"<VOIP>" +
	"<Command-Name>ConferenceInvite</Command-Name>" +
	"<Command-Arg>"+CurrentConference.ConfID+"</Command-Arg>" +
	"<Command-ID></Command-ID>" +
	"<Command-Info>";

	for (var i = 0; i < ExtNumList.length; i++)
	{
		xml = xml + "<ExtNumber>"+ExtNumList[i].ExtNum+"</ExtNumber>";
	}

	xml = xml + "</Command-Info>" +
				"</VOIP>";

    DispatchConference.WsSend(xml);

    return 0;
}

//会议结束
DispatchConference.ConferenceEnd = function()
{
    if (!CurrentConference.ConfID)
    {
        return -2;
    }
    
    if (CurrentConference.HostExtNum != DispatchCtrlLib.extnumber)
    {
        console.log("CurrentConference HostExtNum " + CurrentConference.HostExtNum + " is not you!");
        return -3;
    }
    
    var xml = 
    "<VOIP>" +
    "<Command-Name>ConferenceKick</Command-Name>" +
    "<Command-Arg>"+CurrentConference.ConfID+"</Command-Arg>" +
    "<Command-ID></Command-ID>" +
    "<Command-Info>" +
        "<ExtNumber>all</ExtNumber>" +
    "</Command-Info>" +
    "</VOIP>";

	DispatchConference.WsSend(xml);

	DispatchConference.Stop();

	return 0;
}

//会议踢人
DispatchConference.ConferenceKick = function(ExtNum)
{
    if (!CurrentConference.ConfID)
    {
    	return -2;
    }
    
    if (CurrentConference.HostExtNum != DispatchCtrlLib.extnumber)
    {
        console.log("CurrentConference HostExtNum " + CurrentConference.HostExtNum + " is not you!");
        return -3;
    }
    
    var xml = 
    "<VOIP>" +
    "<Command-Name>ConferenceKick</Command-Name>" +
    "<Command-Arg>"+CurrentConference.ConfID+"</Command-Arg>" +
    "<Command-ID></Command-ID>" +
    "<Command-Info>" +
    	"<ExtNumber>" + ExtNum + "</ExtNumber>" +
    "</Command-Info>" +
    "</VOIP>";

    DispatchConference.WsSend(xml);

    return 0;
}

//会议成员禁言
DispatchConference.ConferenceMute = function(ExtNum, Mute) {
    if (!CurrentConference.ConfID)
    {
    	return -2;
    }
    
    if (ExtNum != DispatchCtrlLib.extnumber && CurrentConference.HostExtNum != DispatchCtrlLib.extnumber)
    {
        console.log("CurrentConference HostExtNum " + CurrentConference.HostExtNum + " is not you!");
        return -3;
    }
    
    var xml = 
    "<VOIP>" +
    "<Command-Name>ConferenceMute</Command-Name>" +
    "<Command-Arg>"+CurrentConference.ConfID+"</Command-Arg>" +
    "<Command-ID></Command-ID>" +
    "<Command-Info>" +
    	"<ExtNumber>" + ExtNum + "</ExtNumber>" +
		"<Mute>" + Mute + "</Mute>" +
    "</Command-Info>" +
    "</VOIP>";

    DispatchConference.WsSend(xml);

	return 0;
}

//会议设置音量
DispatchConference.ConferenceSetVolume = function(Volume)
{
    if (!CurrentConference.ConfID)
    {
        return -2;
    }

    if (typeof Volume == "number")
    {
        return DispatchRtcLib.VoipSetCallVolume(CurrentConference.CallID, Volume);
    }
	else
	{
        return -1;
    }
}

//会议切换本地扬声器
DispatchConference.ConferenceSetSpeaker = function(DevLabel)
{
    if (!CurrentConference.ConfID)
    {
    	return -2;
    }

	return DispatchRtcLib.VoipSetSpeaker(CurrentConference.CallID, DevLabel);
}

//会议切换本地麦克风
DispatchConference.ConferenceSetMicphone = function(DevLabel)
{
	if (!CurrentConference.ConfID)
	{
		return -2;
	}

	return DispatchRtcLib.VoipSetMicphone(CurrentConference.CallID, DevLabel);
}

//会议切换本地摄像头
DispatchConference.ConferenceSetCamera = function(DevLabel) {

	if (!CurrentConference.ConfID)
	{
		return -2;
	}

	return DispatchRtcLib.VoipSetCamera(CurrentConference.VideoPush_CallID, DevLabel)
}

//本地禁言
DispatchConference.ConferenceLocalMicMute = function(Mute)
{
    if (!CurrentConference.ConfID)
    {
    	return -2;
    }

    var xml =
    "<VOIP>" +
    "<Command-Name>ConferenceMute</Command-Name>" +
    "<Command-Arg>"+CurrentConference.ConfID+"</Command-Arg>" +
    "<Command-ID></Command-ID>" +
    "<Command-Info>" +
    	"<ExtNumber>" + DispatchCtrlLib.extnumber + "</ExtNumber>" +
		"<Mute>" + Mute + "</Mute>" +
    "</Command-Info>" +
    "</VOIP>";

    DispatchConference.WsSend(xml);

	return 0;
}

//会议视频观看
DispatchConference.ConferenceWatch = function(ExtNum, VideoLib)
{
	if (!CurrentConference.ConfID)
	{
		return -2;
	}

	for (var i = 0; i < CurrentConference.MemberList.length; i++)
	{
		if (CurrentConference.MemberList[i].ExtNum == ExtNum)
		{
			if (CurrentConference.MemberList[i].PullCallID == 0)
			{
				var CallID = ScsConstants.VoipCallID++;

				var OnMakeCallSucess = function(sdp)
				{
					CurrentConference.MemberList[i].PullCallID = CallID;

					var xml =
				    "<VOIP>" +
				    "<Command-Name>ConferenceVideoPullReq</Command-Name>" +
				    "<Command-Arg>"+CurrentConference.ConfID+"</Command-Arg>" +
				    "<Command-ID>"+CurrentConference.ConfID+"-"+ExtNum+"-"+DispatchCtrlLib.extnumber+"</Command-ID>" +
				    "<Command-Info>" +
				    	"<ExtNum>" + DispatchCtrlLib.extnumber + "</ExtNum>" +
						"<Target>" + ExtNum + "</Target>" +
				    "</Command-Info>" +
				    "</VOIP>\r\n";

					xml = xml + sdp;

					//CurrentConference.MemberList[i].VideoLib = VideoLib;
					DispatchRtcLib.DisplayVideo(CallID, VideoLib);

				    DispatchConference.WsSend(xml);
				}

				var OnMakeCallFail = function(error)
				{
				    console.log("DispatchConference.PullVideo Fail:" + error);
				}

				return DispatchRtcLib.CreateOffer(CallID, DispatchRtcLib_ConfVideoPull, OnMakeCallSucess, OnMakeCallFail);
			}

			return -1;
		}
	}

	return -1;
}

//会议视频聚焦
DispatchConference.ConferenceFocus = function(ExtNum)
{
    if (!CurrentConference.ConfID)
    {
    	return -2;
    }

    var xml =
    "<VOIP>" +
    "<Command-Name>ConferenceFocus</Command-Name>" +
    "<Command-Arg>"+CurrentConference.ConfID+"</Command-Arg>" +
    "<Command-ID></Command-ID>" +
    "<Command-Info>" +
    	"<ExtNumber>" + DispatchCtrlLib.extnumber + "</ExtNumber>" +
    "</Command-Info>" +
    "</VOIP>";

    DispatchConference.WsSend(xml);
}

//会议开始推送
DispatchConference.VideoPushReq = function() {
	var CallID = ScsConstants.VoipCallID++;

	var OnMakeCallSucess = function(sdp)
	{
	    CurrentConference.VideoPush_CallID = CallID;

		var xml =
	    "<VOIP>" +
	    "<Command-Name>ConferenceVideoPushReq</Command-Name>" +
	    "<Command-Arg>"+CurrentConference.ConfID+"</Command-Arg>" +
	    "<Command-ID>"+CurrentConference.ConfID+"-"+DispatchCtrlLib.extnumber+"</Command-ID>" +
	    "<Command-Info>" +
	    	"<ExtNum>" + DispatchCtrlLib.extnumber + "</ExtNum>" +
	    "</Command-Info>" +
	    "</VOIP>\r\n";

		xml = xml + sdp;

	    DispatchConference.WsSend(xml);
	}

	var OnMakeCallFail = function(error)
	{
	    console.log("DispatchConference.PushLocalVideo Fail:" + error);
	}

	return DispatchRtcLib.CreateOffer(CallID, DispatchRtcLib_ConfVideoPush, OnMakeCallSucess, OnMakeCallFail);
}

//视频推送应答
DispatchConference.VideoPushRsp = function(answer)
{
	DispatchRtcLib.SetRemoteSdp(CurrentConference.VideoPush_CallID, answer, DispatchRtcLib_ConfVideoPush);
}

//视频拉取应答
DispatchConference.VideoPullRsp = function(CallID, answer)
{
	DispatchRtcLib.SetRemoteSdp(CallID, answer, DispatchRtcLib_ConfVideoPull);
}


//会议移交主持人
DispatchConference.ChangeHost = function(ExtNum) 
{
    if (CurrentConference.HostExtNum != DispatchCtrlLib.extnumber)
    {
        console.log("CurrentConference HostExtNum " + CurrentConference.HostExtNum + " is not you!");
        return -2;
    }
    
    var xml =
    "<VOIP>" +
    "<Command-Name>ConferenceHostChange</Command-Name>" +
    "<Command-Arg>"+CurrentConference.ConfID+"</Command-Arg>" +
    "<Command-ID></Command-ID>" +
    "<Command-Info>" +
        "<ExtNum>" + DispatchCtrlLib.extnumber + "</ExtNum>" +
        "<Host>" + ExtNum + "</Host>" +
    "</Command-Info>" +
    "</VOIP>\r\n";

    DispatchConference.WsSend(xml);
    
    return 0;
}
export { DispatchRtcLib }
export { DispatchConference }
export { DispatchRtcLib_VoipVideo }
export { DispatchRtcLib_MoniterVideo }
export { DispatchRtcLib_VoipAudio }
export { DispatchDevices }
